package org.hk.proxy.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.hk.proxy.client.config.ClientConfig;
import org.hk.proxy.client.handlers.ClientChannelHandler;
import org.hk.proxy.client.handlers.PingHandler;
import org.hk.proxy.client.handlers.RealServerChannelHandler;
import org.hk.proxy.client.listener.ChannelStatusListener;
import org.hk.proxy.common.container.Container;
import org.hk.proxy.common.container.ContainerHelper;
import org.hk.proxy.protocol.ProxyMessage;
import org.hk.proxy.protocol.ProxyMessageDecoder;
import org.hk.proxy.protocol.ProxyMessageEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

public class ProxyClientContainer implements Container, ChannelStatusListener {
    private static final Logger log = LoggerFactory.getLogger(ProxyClientContainer.class);
    private static final int MAX_FRAME_LENGTH = 1024 * 1024;
    private static final int LENGTH_FIELD_OFFSET = 0;
    private static final int LENGTH_FIELD_LENGTH = 4;
    private static final int INITIAL_BYTES_TO_STRIP = 0;
    private static final int LENGTH_ADJUSTMENT = 0;
    private NioEventLoopGroup workerGroup;
    private Bootstrap bootstrap;
    private Bootstrap realServerBootstrap;
    private long sleepTimeMill = 1000;
    private  ScheduledExecutorService scheduledExecutorService1 = Executors.newScheduledThreadPool(10);


    public ProxyClientContainer() {
        workerGroup = new NioEventLoopGroup(10);
        realServerBootstrap = new Bootstrap();
        realServerBootstrap.group(workerGroup);
        realServerBootstrap.channel(NioSocketChannel.class);
        realServerBootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch){
                ch.pipeline().addLast(new RealServerChannelHandler());
            }
        });
        bootstrap = new Bootstrap();
        bootstrap.group(workerGroup);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch){
                ch.pipeline().addLast(new ProxyMessageDecoder(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP));
                ch.pipeline().addLast(new ProxyMessageEncoder());
                ch.pipeline().addLast(new PingHandler(20,10,30));
                ch.pipeline().addLast(new ClientChannelHandler(realServerBootstrap,bootstrap,ProxyClientContainer.this));
            }
        });
    }
    private void  connectProxyServer(){
        bootstrap.connect(ClientConfig.CLIENT_CONFIG.getServerIp(),ClientConfig.CLIENT_CONFIG.getServerPort()).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future){
                if(future.isSuccess()){
                    // 连接成功，向服务器发送客户端认证信息（clientKey）
                    log.info("客户端主动连接成功");
                    ProxyMessage msg = new ProxyMessage();
                    msg.setType(ProxyMessage.C_TYPE_AUTH);
                    msg.setUri(ClientConfig.CLIENT_CONFIG.getClientKey());
                    ClientChannelManagement.cmdChannel = future.channel();
                    future.channel().writeAndFlush(msg);
                    sleepTimeMill = 1000;
                    ClientChannelManagement.returnProxyChannel(future.channel());
                    log.info("connect proxy server success, {}", future.channel());
                }else {
                    reconnectWait();
                    connectProxyServer();
                }
            }
        });
    }
    private void reconnectWait() {
        try {
            if (sleepTimeMill > 60000) {
                sleepTimeMill = 1000;
            }
            synchronized (this) {
                sleepTimeMill = sleepTimeMill * 2;
                wait(sleepTimeMill);
            }
        } catch (InterruptedException e) {
        }
    }
    @Override
    public void start() {
        connectProxyServer();
    }
    @Override
    public void stop() {
        workerGroup.shutdownGracefully();
    }

    public static void main(String[] args) {
        ContainerHelper.start(Arrays.asList(new Container[] { new ProxyClientContainer() }));
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        reconnectWait();
        connectProxyServer();
    }
}
